There is an increased trend in writing software using managed languages in order to improve productivity and reduce the incidence of certain classes of bugs. Managed languages, such as Java and C++, generally include mechanisms for exchanging information between the program and the runtime environment for interprocedural control flow. At any point of execution, the runtime can stop the CPU (Central Processing Unit) and retrieve information specific to the current CPU instruction address, and runtime state, such as register or stack contents.
With regard to exception flow control processing, there are two basic methods of managing stack operations: stack unwinding (exception throwing) and stack cutting. Stack unwinding is typically provided as a mechanism in most managed languages and works by unwinding stack frames one at a time until a handler for the exception is found, and then discarding intervening stack frames and resuming execution at the handler. This method is generally safe and facilitates program composability, which is the ability to define and combine program components without undue side effects; however, it can be fairly expensive in terms of processor overhead if the handler is far from the exception entry point. Stack cutting is generally faster than exception throwing, and works by directly resuming execution according to a snapshot of an execution state that was previously taken, and discarding intervening stack frames. Stack cutting is explicitly provided in some languages, such as continuations in certain functional languages. It can also be implicit in some language constructs. For example, in a transactional memory construct, there is the expectation that a data conflict will roll back the transactional state and restart the transaction, which may involve cutting back several stack frames from the point of the data conflict to the beginning of the transaction. Though the stack cutting method requires the definition of a data structure to hold the execution context and an associated call chain, the cut operation itself is typically very fast.
Many language constructs require a “destructor” or “undo” operation when control flow exits the construct. Thus, if an object is declared locally within a function, the function usually contains code necessary to do the destructor operation to remove the object. For example, in Java or C#, when leaving a synchronized method or block, the synchronization object needs to be unlocked. Likewise, when leaving a transactional block in a transactional memory system, the transaction needs to be closed or ended. The exception throwing process is based on stack unwinding, which provides for the examination of every intervening stack frame between the throw and the handler. As such, it is relatively easy to design a mechanism for marking destructor operations in each stack frame and making sure the destructors are executed when doing an exception unwind. Stack cutting, however, operates quite differently and is generally considered to have a constant time for setting up and jumping to the new execution context, which precludes frame-by-frame stack walking. This cheaper cost model is why stack cutting is often used instead of exception throwing. However, stack cutting is often a risky and dangerous construct and must be used with great care, since the implementation simply ignores and discards destructor operations in intervening stack frames. For example, if an “end-transaction” command is in an intermediate stack frame that is ignored and discarded by a stack cut operation, a transaction could be left open, thus leading to a deadlock situation. Therefore, despite its ability to provide performance advantages, using stack cutting in present systems can be problematic and dangerous. Moreover, present stack cutting implementations are generally not composable with other constructs since mechanisms are not provided to ensure that any pending processes are properly resolved during a stack cut operation.